package educrypt.cryptanalysis.hill;

import educrypt.ciphers.hill.HillKeyMatrix;
import educrypt.ciphers.hill.HillNgrams;
import educrypt.commons.Alphabet;
import educrypt.commons.Text;
import educrypt.exceptions.EduCryptException;
import educrypt.i18n.Messages;
import educrypt.log.ObservableLogger;
import educrypt.log.SimpleLogger;

public class CryptAnalysisHill {
	private Alphabet alphabet;
	private Text cipherText;

	public CryptAnalysisHill(Alphabet alphabet, Text cipherText) {
		this.alphabet = alphabet;
		this.cipherText = cipherText;
	}

	public HillKeyMatrix cryptAnalyze(String plainTextChunk, int ngramSize)
			throws EduCryptException {

		SimpleLogger logger = ObservableLogger.getInstance();
		logger.log(SimpleLogger.TITLE,
				"---- " + Messages.getString("guiKnownPlaintext") + " ----");

		String cipherTextChunck = cipherText.getContent().substring(0,
				plainTextChunk.length());
		HillNgrams ngramsP = new HillNgrams(plainTextChunk, alphabet, ngramSize);

		HillNgrams ngramsC = new HillNgrams(cipherTextChunck, alphabet,
				ngramSize);

		if (ngramsP.size() < ngramSize || ngramsC.size() < ngramSize) {
			throw new EduCryptException(Messages.getString("errNotEnoughText"));
		}

		double[][] plainMatrix = new double[ngramSize][ngramSize];
		double[][] cipherMatrix = new double[ngramSize][ngramSize];

		HillKeyMatrix key;

		boolean resultFound = false;
		int sum = 0;
		do {
			for (int i = 0; i < ngramSize; i++)
				if (ngramsP.size() <= i + sum || ngramsC.size() <= i + sum) {
					throw new EduCryptException(
							Messages.getString("errNotEnoughText"));
				} else {
					String plainNgram = ngramsP.get(i + sum).getRealNgram();
					// System.out.println(plainNgram);
					String cipherNgram = ngramsC.get(i + sum).getRealNgram();
					// System.out.println(cipherNgram);
					for (int j = 0; j < ngramSize; j++) {
						plainMatrix[j][i] = alphabet
								.getSymbolPosition(plainNgram.charAt(j));
						cipherMatrix[j][i] = alphabet
								.getSymbolPosition(cipherNgram.charAt(j));
					}
				}

			HillKeyMatrix plainHKM = new HillKeyMatrix(plainMatrix,
					alphabet.size());
			resultFound = plainHKM.isInversible();
			// System.out.println(plainHKM);

			HillKeyMatrix cipherHKM = new HillKeyMatrix(cipherMatrix,
					alphabet.size());

			logger.log(SimpleLogger.TITLE,
					Messages.getString("guiGeneratedPlainMatrix"));
			logger.log(SimpleLogger.CONTENT,
					String.valueOf(plainHKM.toString()));

			logger.log(SimpleLogger.TITLE,
					Messages.getString("guiGeneratedCipherMatrix"));
			logger.log(SimpleLogger.CONTENT, cipherHKM.toString());

			if (!resultFound) {

				logger.log(SimpleLogger.TITLE,
						Messages.getString("guiGeneratedPlainMatrixNoInverse"));
				logger.log(SimpleLogger.CONTENT, " . . . ");
				logger.log("");
			}

			// System.out.println(cipherHKM);
			key = cipherHKM.multiply(plainHKM.inverse());
			// System.out.println(key);
			if (resultFound) {
				logger.log(SimpleLogger.TITLE,
						Messages.getString("guiHillKeyMatrixFound"));
				logger.log(SimpleLogger.CONTENT, key.toString());
				logger.log("");
			}
			sum++;
		} while (!resultFound);
		return key;
	}
}
